package cn.edu.hyit.hjq.volunteems.backend.filter;

import cn.edu.hyit.hjq.volunteems.backend.service.RedisService;
import cn.edu.hyit.hjq.volunteems.backend.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.List;
import java.util.Map;

@Slf4j
@Component
public class TokenAuthFilter extends OncePerRequestFilter {

    private final RedisService redisService;
    private final JwtUtils jwtUtils; // 新增 JwtUtils 注入

    private static final String OPTIONS = "OPTIONS";

    public TokenAuthFilter(RedisService redisService, JwtUtils jwtUtils) {
        this.redisService = redisService;
        this.jwtUtils = jwtUtils;
    }

    /**
     * 执行实际的过滤逻辑
     *
     * @param request HttpServletRequest对象，用于获取请求信息
     * @param response HttpServletResponse对象，用于设置响应信息
     * @param filterChain FilterChain对象，用于将当前过滤器与下一个过滤器连接起来
     * @throws ServletException 如果过滤过程中发生Servlet异常
     * @throws IOException 如果过滤过程中发生IO异常
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        String token = request.getHeader("Authorization");

        if (token != null && token.startsWith("Bearer ")) {
            token = token.substring(7);

            try {
                // 第一步：验证 JWT 签名是否合法
                Claims claims = jwtUtils.parseToken(token);
                String userId = claims.getSubject(); // 从 JWT 获取 subject，即用户ID或用户名

                // 第二步：验证 Token 是否存在于 Redis
                Map<Object, Object> tokenData = validateToken(token);
                if (tokenData == null || tokenData.isEmpty()) {
                    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token has been revoked or is invalid");
                    log.error("Token has been revoked or is invalid: {}", token);
                    return;
                }

                // 第三步：从 Redis 获取角色和用户状态
                String role = (String) tokenData.get("role");
                String status = (String) tokenData.get("status");
                int state = status != null ? Integer.parseInt(status) : -1;

                // 检查用户状态是否有效（如 state == 0 表示未激活/禁用）
                if (state != 1) {
                    response.sendError(HttpServletResponse.SC_FORBIDDEN, "User account is not active");
                    log.error("User account is not active: {}", userId);
                    return;
                }

                // 第四步：构建认证信息，使用 Redis 中的角色
                if (userId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                    List<SimpleGrantedAuthority> authorities = List.of(new SimpleGrantedAuthority(role));

                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                            userId, null, authorities);

                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }

            } catch (JwtException e) {
                // JWT 解析失败或签名不合法
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid JWT token");
                return;
            } catch (Exception e) {
                // Redis 异常或其他错误
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Authentication failed");
                return;
            }
        }

        filterChain.doFilter(request, response);
    }

    private Map<Object, Object> validateToken(String token) {
        return redisService.getHashValues(token);
    }
}